package com.bwie.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bwie.pojo.TbOrders;
import com.bwie.pojo.vo.GrabOrderVo;
import com.bwie.pojo.vo.UserVo;
import com.bwie.service.TbOrdersService;
import com.bwie.mapper.TbOrdersMapper;
import com.bwie.utils.R;
import lombok.extern.slf4j.Slf4j;
import org.redisson.RedissonLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.geo.*;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

/**
 * @author xinH
 * @description 针对表【tb_orders(订单表)】的数据库操作Service实现
 * @createDate 2025-09-08 14:37:59
 */
@Slf4j
@Service
public class TbOrdersServiceImpl extends ServiceImpl<TbOrdersMapper, TbOrders>
        implements TbOrdersService {

    private static final String ORDER_KEY = "order";


    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private TbOrdersMapper ordersMapper;

    @Override
    public R downOrder(TbOrders orders) {

        //  添加用户地理位置
        Long addedCount = redisTemplate.opsForGeo().add(
                ORDER_KEY,
                new RedisGeoCommands.GeoLocation<>(
                        orders.getUserId(),
                        new Point(orders.getStartLongitude(), orders.getStartLatitude())
                )
        );

        // 判断是否添加成功
        if (addedCount != null && addedCount > 0) {
            log.info("用户 {} 地理位置添加成功", orders.getUserId());
        } else {
            log.error("用户 {} 地理位置添加失败", orders.getUserId());
            // 可抛出异常或返回错误提示
        }

        //  创建订单对象并赋值
        TbOrders order = new TbOrders();
        order.setUserId(order.getUserId());
        order.setStartLatitude(order.getStartLatitude());
        order.setStartLongitude(orders.getStartLongitude());
        order.setStatus(0);// 初始状态：待抢单
        order.setPrice(orders.getPrice());
        order.setCreatedAt(new Date());
        order.setStartLocation(orders.getStartLocation());
        order.setEndLocation(orders.getEndLocation());

        // 保存订单到数据库
        ordersMapper.insert(order);


        return R.OK();
    }

    @Override
    public void userToRedisGEO(UserVo userVo) {
        Long addedCount = redisTemplate.opsForGeo().add(
                "order-" + userVo.getUserId(),
                new RedisGeoCommands.GeoLocation<>(
                        userVo.getUserId(),
                        new Point(userVo.getLongitude(), userVo.getLatitude())
                )
        );

        // 判断是否添加成功
        if (addedCount != null && addedCount > 0) {
            log.info("用户 {} 地理位置添加成功", userVo.getUserId());
        } else {
            log.error("用户 {} 地理位置添加失败", userVo.getUserId());
            // 可抛出异常或返回错误提示

        }
    }

    @Override
    public List<GeoResult<RedisGeoCommands.GeoLocation<String>>> nearbyDriver(double longitude, double latitude, double radiusKm, long count) {

        //创建圆形区域：以给定经纬度为中心，半径为radiusKm的圆形区域
        Circle circle = new Circle(new Point(longitude, latitude), new Distance(radiusKm, Metrics.KILOMETERS));

        //执行Redis GEO查询：在Redis中查找ORDER_KEY键下，位于该圆形区域内的地理位置数据
        //设置查询参数：包含距离、坐标信息，按距离升序排列，限制返回数量
        GeoResults<RedisGeoCommands.GeoLocation<String>> results =
                redisTemplate.opsForGeo().radius(ORDER_KEY, circle,
                        RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs()
                                .includeDistance()  // 返回距离
                                .includeCoordinates() // 返回坐标
                                .sortAscending()     // 按距离升序
                                .limit(count)        // 限制返回数量
                );
        log.info("查询结果：{}", results.getContent());
        //返回结果：返回查询到的地理位置信息列表
        return results.getContent();
    }

    @Override
    public R distance(String placeId1, String placeId2, String unit) {


        // 转换单位为Redis枚举（m=米，其他默认千米）
        RedisGeoCommands.DistanceUnit distanceUnit = "m".equals(unit)
                ? RedisGeoCommands.DistanceUnit.METERS
                : RedisGeoCommands.DistanceUnit.KILOMETERS;

        // 调用Redis GEO的`distance`方法计算两点距离
        Distance distance = redisTemplate.opsForGeo()
                .distance("taxi:locations", placeId1, placeId2, distanceUnit);

        log.info("距离：{}", distance);
        // 若距离计算有效，返回距离值；否则返回null
        return R.OK(distance);


    }

    @Override
    public boolean driverGrabOrder(GrabOrderVo grabOrderVo) {

        String lockKey = "lock:order:" + grabOrderVo.getOrderId();
        try {
            TbOrders tbOrders = ordersMapper.selectById(grabOrderVo.getOrderId());
            if (tbOrders == null) {
                throw new RuntimeException("订单不存在");
            }

            //上锁
            Boolean aBoolean = redisTemplate.opsForValue()
                    .setIfAbsent(lockKey, grabOrderVo.getOrderId() + "", 3, TimeUnit.SECONDS);
            if (Boolean.FALSE.equals(aBoolean)) {
                throw new RuntimeException("有用户正在抢单");
            }

            // 查订单是否已被抢
            TbOrders order = ordersMapper.selectById(grabOrderVo.getOrderId());
            if (order == null || order.getDriverId() != null) {
                return false;
            }

            // 抢单：更新订单的司机ID
            order.setDriverId(grabOrderVo.getDriverId());
            return ordersMapper.updateById(order) > 0;
        } catch (RuntimeException e) {
            e.printStackTrace();
            return false;
        // throw new RuntimeException(e);
        } finally {
            //释放锁
            redisTemplate.delete(lockKey);
        }
    }
}

